//
// Created by root on 2020/3/30.
//

#include "Server.h"

#include <muduo/base/Logging.h>
#include <muduo/base/Singleton.h>

#include "User.h"
#include "UserMgr.h"
#include "ProtocolNoDefine.h"
#include "CommonFunction.h"

using namespace std::placeholders;

CServer::CServer(muduo::net::EventLoop *_pEventLoop, muduo::net::InetAddress &_oInetAddress)
: m_oTcpServer(_pEventLoop, _oInetAddress, "Sever"), m_pLoop(_pEventLoop)
{
    m_oTcpServer.setConnectionCallback(std::bind(&CServer::onConnection, this, _1));
    m_oTcpServer.setMessageCallback(std::bind(&CServer::onMessage, this, _1, _2, _3));
    m_pLoop->runEvery(60, std::bind(&CServer::onCheckRegular, this));
    m_pLoop->runEvery(1, std::bind(&CServer::onCheckUnLoginConn, this));
    m_pPackHandler = std::make_unique<CPackHandle>();
}

CServer::~CServer()
{

}

void CServer::Start()
{
    m_oTcpServer.start();
}

void CServer::onConnection(const muduo::net::TcpConnectionPtr &_pTcpConn)
{
    LOG_INFO << "EchoServer - " << _pTcpConn->peerAddress().toIpPort() << " -> "
             << _pTcpConn->localAddress().toIpPort() << " is "
             << (_pTcpConn->connected() ? "UP" : "DOWN");
    if (_pTcpConn->connected())
    {
        m_vtUnLoginConn.push_back({_pTcpConn, commonfunction::GetCurrentTimestamp()});
    }
    else{
        if (!gUserMgr::instance().Delete(_pTcpConn->name()))
        {
            this->_RemoveUnLoginConn(_pTcpConn->name());
        }
    }
}

void CServer::onCheckRegular() {
    muduo::Singleton<CUserMgr>::instance().CheckRegular();
}

void CServer::onMessage(const muduo::net::TcpConnectionPtr &_pTcpConn, muduo::net::Buffer *_pBuffer,
                        muduo::Timestamp _oTimeStamp) {
    LOG_INFO << "onMessage IP:" << _pTcpConn->peerAddress().toIp() << " Port:" << _pTcpConn->peerAddress().toIp();
    while (_pBuffer->readableBytes() > 4) {
        const void *pData = _pBuffer->peek();
        int32_t be32 = *static_cast<const int32_t *>(pData);
        int32_t len = muduo::net::sockets::networkToHost32(be32);  // 大端序转本地子字节序
        if (len > 65536 || len < 0) {
            LOG_ERROR << "Invalid length " << len;
            _pTcpConn->shutdown();
            // 保存的Tcp客户端连接
            if (!gUserMgr::instance().Delete(_pTcpConn->name()))
            {
                this->_RemoveUnLoginConn(_pTcpConn->name());
            }
            return;
        }
        else if (_pBuffer->readableBytes() >= len + 4) {
            _pBuffer->retrieve(4);      // 跳过报文头部字节数
            if (len < 4)    // 无效报文
            {
                _pBuffer->retrieve(len);
                return;
            }
            const void *pData = _pBuffer->peek();
            int32_t beProtocolNo = *static_cast<const int32_t *>(pData);
            int32_t iProtocolNo = muduo::net::sockets::networkToHost32(beProtocolNo);
            _pBuffer->retrieve(4);  //跳过协议号

            muduo::Singleton<CUserMgr>::instance().UpdateRegular(_pTcpConn->name());
            std::string sJson(_pBuffer->peek(), len);
            m_pPackHandler->HandleCommand(_pTcpConn, iProtocolNo, sJson);
            _pBuffer->retrieve(len - 4);       // 跳过报文体字节长度的
        } else {
            return;
        }
    }
}

void CServer::onCheckUnLoginConn() {
    uint32_t  uiCurTimeStamp = commonfunction::GetCurrentTimestamp();
    auto iter = m_vtUnLoginConn.begin();
    for (; iter != m_vtUnLoginConn.end(); )
    {
        if (uiCurTimeStamp - iter->second > 3000)
        {
            muduo::net::TcpConnectionPtr pClient = iter->first.lock();
            if (pClient)
            {
                pClient->shutdown();
            }
            iter = m_vtUnLoginConn.erase(iter); // 返回删除节点后一个节点迭代器
        }
        else{
            iter++;
        }
    }
}

void CServer::_RemoveUnLoginConn(const std::string &_csConnName) {
    auto iter = m_vtUnLoginConn.begin();
    for (; iter != m_vtUnLoginConn.end(); iter++)
    {
        if (const muduo::net::TcpConnectionPtr pConn = iter->first.lock(); pConn)
        {
            if (pConn->name() == _csConnName)
            {
                m_vtUnLoginConn.erase(iter);
                break;
            }
        }
    }
}
